#include "buttondevice.h"
#include <map>
#include "soc/soc_gpio.h"
#include "log/logger.h"

static Timer m_updateTimer;

ButtonDevice::BUTTON_EVENT ButtonDevice::m_eventFuncAny = nullptr;

uint8_t __ReadInputLevel(uint8_t id)
{
   auto btnDev = ButtonDevice::GetDeviceById(id);
   return (btnDev)? btnDev->ReadInputLevel():0;
}

void __DownCallBack(void *button)
{
   auto btnDev = ButtonDevice::GetDeviceById(((Button*)button)->button_id);
   if(btnDev)
      btnDev->RaiseEventCallBack(ButtonDevice::ButtonAction::Down);
}

void __UpCallBack(void *button)
{
   auto btnDev = ButtonDevice::GetDeviceById(((Button*)button)->button_id);
   if(btnDev)
      btnDev->RaiseEventCallBack(ButtonDevice::ButtonAction::Up);
}

void __DownLongCallBack(void *button)
{
   auto btnDev = ButtonDevice::GetDeviceById(((Button*)button)->button_id);
   if(btnDev)
      btnDev->RaiseEventCallBack(ButtonDevice::ButtonAction::DownLong);
}

void __DoubleClickCallBack(void *button)
{
   auto btnDev = ButtonDevice::GetDeviceById(((Button*)button)->button_id);
   if(btnDev)
      btnDev->RaiseEventCallBack(ButtonDevice::ButtonAction::DoubleClick);
}

ButtonDevice::ButtonDevice()
{
   
}

ButtonDevice::ButtonDevice(int id, GpioPortPin portPin, int activeLevel)
{
   SetInfo(id, portPin, activeLevel);
}

ButtonDevice::ButtonDevice(int id, const std::string &name, GpioPortPin portPin, int activeLevel)
{
   SetInfo(id, name, portPin, activeLevel);
}

ButtonDevice::~ButtonDevice()
{
   RemoveDevice(m_id);
   button_stop(&m_button);
}

void ButtonDevice::SetInfo(int id, GpioPortPin portPin, int activeLevel)
{
   return SetInfo(id, "", portPin, activeLevel);
}

void ButtonDevice::SetInfo(int id, const std::string &name, GpioPortPin portPin, int activeLevel)
{
   m_id = id;
   m_name = name;
   m_activeLevel = activeLevel;
   m_portPin = portPin;
   
   __AddToDeviceList();
}

int ButtonDevice::GetId()
{
   return m_id;
}

std::string ButtonDevice::GetName()
{
   return m_name;
}

GpioPortPin ButtonDevice::GetPortPin()
{
   return m_portPin;
}

int ButtonDevice::GetActiveLevel()
{
   return m_activeLevel;
}

bool ButtonDevice::GetDownState()
{
   return m_isDown;
}

bool ButtonDevice::GetLongDownState()
{
   return m_isLongDown;
}

void ButtonDevice::Start()
{
   __Init();
}

void ButtonDevice::Stop()
{
   if(m_updateTimer.IsRunning())
      m_updateTimer.Stop();
   button_stop(&m_button);
}

void ButtonDevice::SetEventListener(const BUTTON_EVENT &&func)
{
   m_eventFunc = func;
}

void ButtonDevice::SetEventListenerAny(const BUTTON_EVENT &&func)
{
   m_eventFuncAny = func;
}

void ButtonDevice::StartAll()
{
   ForeachAll([=](auto dev){
      dev->Start();
   });
}

void ButtonDevice::StopAll()
{
   ForeachAll([=](auto dev){
      dev->Stop();
   });
}

uint8_t ButtonDevice::ReadInputLevel()
{
   return SOC_Gpio::Read(m_portPin);
}

void ButtonDevice::RaiseEventCallBack(ButtonAction action)
{
   if(m_eventFunc != nullptr)
      m_eventFunc(*this, action);

   if(m_eventFuncAny != nullptr)
      m_eventFuncAny(*this, action);
}

void ButtonDevice::__AddToDeviceList()
{
   AddDevice(m_id, this);
}

void ButtonDevice::__Init()
{
   if(m_id < 0)
      return;

   //先停止
   button_stop(&m_button);
   //
   AddDevice(m_id, this);
   button_init(&m_button, __ReadInputLevel, m_activeLevel, m_id);
   button_attach(&m_button, PressEvent::PRESS_DOWN, __DownCallBack);
   button_attach(&m_button, PressEvent::PRESS_UP, __UpCallBack);
   button_attach(&m_button, PressEvent::DOUBLE_CLICK, __DoubleClickCallBack);
   button_attach(&m_button, PressEvent::LONG_PRESS_HOLD, __DownLongCallBack);
   button_start(&m_button);

   //
   if(!m_updateTimer.IsRunning())
   {
      m_updateTimer.SetInterval(BUTTON_DEV_TICK_MS);
      m_updateTimer.SetElapsed([](auto &timer){button_ticks();});
      m_updateTimer.Start();
   }
}
